/**
 *
 */
package com.glodon.jcloud.increment.shorturl.api;

import com.glodon.jcloud.common.api.APIResponse;
import com.glodon.jcloud.common.api.ApiStatus;
import com.glodon.jcloud.common.exception.ApiException;
import com.glodon.jcloud.common.security.AccessToken;
import com.glodon.jcloud.common.transfer.OAuth2GrantType;
import com.glodon.jcloud.common.transfer.OAuth2Request;
import com.glodon.jcloud.common.utils.MD5;
import com.glodon.jcloud.increment.shorturl.entities.OAuth2Client;
import com.glodon.jcloud.increment.shorturl.security.TokenUtils;
import com.glodon.jcloud.increment.shorturl.service.OAuth2ClientService;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.time.Clock;
import java.util.Map;

import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
 * Terry MicroService
 *
 * Create Date: 2017年9月13日 上午11:33:37
 *
 * @version: V0.0.1-SNAPSHOT
 * @author: Terry.Li
 * @mail: lib-o@glodon.com
 */
@RestController
@RequestMapping("/api/oauth2")
public class OAuth2API extends BaseAPI {

	private static final long serialVersionUID = 4566171330569847369L;

	@Resource
	private OAuth2ClientService auth2ClientService;
	
	@RequestMapping("/authentication")
	public String authentication(String account, String password, String destination, HttpServletResponse response, ModelMap model) throws UnsupportedEncodingException {
		return "authentication";
	}
	
	@RequestMapping(value = "/authorize", method = { GET, POST }/* , params = "response_type=code" */)
	public String authorize(@RequestParam(required = false, defaultValue = "code") String response_type, String client_id, String redirect_uri, String scope, String state,
			HttpServletResponse response) {
		if (!StringUtils.equals(response_type, "code")) {
			throw new ApiException(ApiStatus.OAUTH2_UNSUPPORTED_RESPONSE_TYPE);
		}
		
		if (StringUtils.isBlank(client_id)) {
			throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT);
		}
		
		// invalid_scope
		// The requested scope is invalid, unknown, or malformed.
		//
		// server_error
		// The authorization server encountered an unexpected
		// condition that prevented it from fulfilling the request.
		// (This error code is needed because a 500 Internal Server
		// Error HTTP status code cannot be returned to the client
		// via an HTTP redirect.)
		//
		// temporarily_unavailable
		// The authorization server is currently unable to handle
		// the request due to a temporary overloading or maintenance
		// of the server. (This error code is needed because a 503
		// Service Unavailable HTTP status code cannot be returned
		// to the client via an HTTP redirect.)
		
		// check client info
		OAuth2Client client = auth2ClientService.getByClientid(client_id);
		
		if (null != client) { // 允许授权
			try {
				// 产生授权码
				String code = MD5.getMD5Str(Clock.systemDefaultZone().millis() + StringUtils.EMPTY);
				if (StringUtils.isBlank(redirect_uri)) {
					redirect_uri = client.getCallbackurl();
				}
				cache.put(code, redirect_uri, 10 * 60);
				String redirect = authorizationCallback(redirect_uri, code, state);
				System.out.println("Redirect URL: " + redirect);
				response.sendRedirect(redirect);
				return code;
			} catch (Exception e) {
				throw new ApiException(ApiStatus.SYSTEM_ERROR);
			}
		} else {
//			throw new ApiException(ApiStatus.OAUTH2_UNAUTHORIZED_CLIENT);
			return "Not find client: " + client_id;
		}
	}

	@PostMapping("/access_token")
	public APIResponse<AccessToken> accessToken(@RequestBody OAuth2Request request) {
		if (StringUtils.isBlank(request.getGrant_type()) || OAuth2GrantType.get(request.getGrant_type()) == null) {
			throw new ApiException(ApiStatus.OAUTH2_UNSUPPORTED_GRANT_TYPE);
		}

		OAuth2GrantType grantType = OAuth2GrantType.get(request.getGrant_type());
		switch (grantType) {
		case authorization_code:
			if (StringUtils.isBlank(request.getClient_id()) || StringUtils.isBlank(request.getClient_secret())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT);
			}

			if (StringUtils.isBlank(request.getCode())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_AUTHORIZATION_CODE);
			}

			if (StringUtils.isBlank(request.getRedirect_uri())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_REDIRECT_URI);
			}

			Object cachedCode = cache.get(request.getCode());
			if (null == cachedCode || !cachedCode.toString().startsWith(request.getRedirect_uri())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_AUTHORIZATION_CODE);
			} else {
				OAuth2Client client = auth2ClientService.getByClientidAndClientsecure(request.getClient_id(), request.getClient_secret());
				if (null == client) {
					throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT, "客户端ID或密码有误");
				}
				return success(createToken(client));
			}
		case password:

			if (StringUtils.isBlank(request.getClient_id()) || StringUtils.isBlank(request.getClient_secret())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT);
			}

			if (StringUtils.isNotBlank(request.getClient_id()) && StringUtils.isNotBlank(request.getClient_secret())) {
				OAuth2Client client = auth2ClientService.getByClientidAndClientsecure(request.getClient_id(), request.getClient_secret());
				if (null == client) {
					throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT, "客户端ID或密码有误");
				}
				return success(createToken(client));
			}

			throw new ApiException(ApiStatus.OAUTH2_UNSUPPORTED_GRANT_TYPE);
		case refresh_token:
			if (StringUtils.isBlank(request.getRefresh_token())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_REFRESH_TOKEN);
			}

			if (StringUtils.isBlank(request.getClient_id()) || StringUtils.isBlank(request.getClient_secret())) {
				throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT);
			}

			Map<String, String> params = (Map<String, String>) cache.get(request.getRefresh_token());
			if (null != params && !params.isEmpty()) {
				System.out.println("Params: " + params.toString());
				cache.remove(request.getRefresh_token());
				return success(TokenUtils.createToken(params));
			}
			
			if (StringUtils.isNotBlank(request.getClient_id()) && StringUtils.isNotBlank(request.getClient_secret())) {
				OAuth2Client client = auth2ClientService.getByClientidAndClientsecure(request.getClient_id(), request.getClient_secret());
				if (null == client) {
					throw new ApiException(ApiStatus.OAUTH2_INVALID_CLIENT, "客户端ID或密码有误");
				}
				return success(createToken(client));
			}
			
			throw new ApiException(ApiStatus.OAUTH2_INVALID_REFRESH_TOKEN);
		case client_credentials:
			throw new ApiException(ApiStatus.OAUTH2_UNSUPPORTED_GRANT_TYPE);
		default:
			break;
		}
		throw new ApiException(ApiStatus.MISS_REQUIRED_PARAMETE);
	}

	private AccessToken createToken(OAuth2Client client) {
		Map<String, String> params = Maps.newHashMap();
		params.put("id", client.getId());
		params.put("client_id", client.getClientid());
		params.put("client_secret", client.getClientsecure());
		params.put("client_name", client.getClientname());
		params.put("sms_platform_id", client.getSmsplatformid());
//		params.put("auto_url_chain", client.getAutoUrlChain() + "");
		return TokenUtils.createToken(params);
	}

	/**
	 * @param redirect_uri
	 * @param code
	 * @param state
	 * @return
	 */
	@SuppressWarnings("unused")
	private String authorizationCallback(String redirect_uri, String code, String state) {
		return redirect_uri + (redirect_uri.indexOf("?") != -1 ? "&" : "?") + "code=" + code + "&state=" + state;
	}

	@RequestMapping("/details")
	public @ResponseBody  OAuth2Client details(@RequestParam String client_id, @RequestParam String client_secret) {
		OAuth2Client client = auth2ClientService.getByClientidAndClientsecure(client_id, client_secret);
		return client;
	}

}
